#include "PackageListModel.h"

#include "Document.h"
#include "ComponentListModel.h"
#include "StringFormatter.h"

#include "LeIpc2581/Package.h"
#include "LeIpc2581/enumtranslator.h"

#include <QModelIndex>
#include <QTransform>
#include <QPointF>
#include <QSortFilterProxyModel>
#include <QVector>

Q_DECLARE_METATYPE(Ipc2581b::Outline*)

struct PackageItem
{
    explicit PackageItem(const Ipc2581b::Package *package = nullptr):
        package(package)
    {}

    const Ipc2581b::Package *package = nullptr;
    QSortFilterProxyModel *componentListModel = nullptr;
};

class PackageListModelPrivate
{
    Q_DISABLE_COPY(PackageListModelPrivate)
    Q_DECLARE_PUBLIC(PackageListModel)
    PackageListModel * const q_ptr;

    PackageListModelPrivate(PackageListModel *model, const Document *document,
                            const QList<Ipc2581b::Package *> &packageList):
        q_ptr(model), document(document), data(packageList.count())
    {
        for (int i = 0; i < packageList.count(); i++)
        {
            data[i].package = packageList.value(i);
            data[i].componentListModel = new QSortFilterProxyModel(q_ptr);
        }
    }

    const Document * const document;
    QVector<PackageItem> data;
};

PackageListModel::PackageListModel(Document *document,
                                   const QList<Ipc2581b::Package *> &packageList):
    QAbstractTableModel(document),
    d_ptr(new PackageListModelPrivate(this, document, packageList))
{
}

void PackageListModel::documentInitialised()
{
    Q_D(PackageListModel);
    for (PackageItem &item: d->data)
    {
        auto proxy = item.componentListModel;
        proxy->setSourceModel(d->document->componentListModel());
        proxy->setFilterKeyColumn(ComponentListModel::PackageRefColumn);
        proxy->setFilterFixedString(item.package->name);
    }
}

PackageListModel::~PackageListModel()
{

}

QAbstractItemModel *PackageListModel::components(int row) const
{
    Q_D(const PackageListModel);

    if (row < 0 || row >= rowCount())
        return nullptr;

    return d->data.at(row).componentListModel;
}

int PackageListModel::rowCount(const QModelIndex &parent) const
{
    Q_D(const PackageListModel);

    if (parent.isValid())
        return 0;

    return d->data.count();
}

int PackageListModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;

    return ColumnCount;
}

QVariant PackageListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation != Qt::Horizontal)
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

    switch (section)
    {
        case NameColumn:
            return "Name";
        case PackageTypeColumn:
            return "Type";
        case PinOneColumn:
            return "Pin1";
        case PinOneOrientationColumn:
            return "Pin1 orientation";
        case HeightColumn:
            return "Height";
        case PickupPointColumn:
            return "Pickup location";
        case PinCountColumn:
            return "Pin count";
        default:
            return QVariant();
    }
}

QVariant PackageListModel::data(const QModelIndex &index, int role) const
{
    Q_D(const PackageListModel);

    if (!index.isValid())
        return QVariant();

    if (index.row() < 0 || index.row() >= rowCount())
        return QVariant();

    const Ipc2581b::Package *package = d->data.value(index.row()).package;
    switch (role)
    {
        case ComponentListModelRole:
            return QVariant::fromValue<QAbstractItemModel*>(d->data.value(index.row()).componentListModel);
        case OutlineRole:
            return QVariant::fromValue<Ipc2581b::Outline*>(d->data.value(index.row()).package->outline);
        case Qt::DisplayRole:
            switch (index.column())
            {
                case NameColumn:
                    return package->name;
                case PackageTypeColumn:
                    return Ipc2581b::EnumTranslator::packageTypeText(package->type);
                case PinOneColumn:
                    if (package->pinOneOptional.hasValue)
                        return package->pinOneOptional.value;
                    return QVariant();
                case PinOneOrientationColumn:
                    if (package->pinOneOrientationOptional.hasValue)
                        return Ipc2581b::EnumTranslator::pinOneOrientationText(package->pinOneOrientationOptional.value);
                    return QVariant();
                case HeightColumn:
                    if (package->heightOptional.hasValue)
                        return package->heightOptional.value;
                    return QVariant();
                case PickupPointColumn:
                    if (package->pickupPointOptional.hasValue)
                        return StringFormatter::pointF(package->pickupPointOptional.value);
                    return QVariant();
                case PinCountColumn:
                    return package->pinList.count();
                default:
                    return QVariant();
            }

        case Qt::EditRole:
            switch (index.column())
            {
                case NameColumn:
                    return package->name;
                case PackageTypeColumn:
                    return QVariant::fromValue<Ipc2581b::PackageType>(package->type);
                case PinOneColumn:
                    if (package->pinOneOptional.hasValue)
                        return package->pinOneOptional.value;
                    return QVariant();
                case PinOneOrientationColumn:
                    if (package->pinOneOrientationOptional.hasValue)
                        return QVariant::fromValue<Ipc2581b::PinOneOrientation>(package->pinOneOrientationOptional.value);
                    return QVariant();
                case HeightColumn:
                    if (package->heightOptional.hasValue)
                        return package->heightOptional.value;
                    return QVariant();
                case PickupPointColumn:
                    if (package->pickupPointOptional.hasValue)
                        return QPointF(package->pickupPointOptional.value->x,
                                       package->pickupPointOptional.value->y);
                    return QVariant();
                case PinCountColumn:
                    return package->pinList.count();
                default:
                    return QVariant();
            }
        default:
            return QVariant();
    }
}
